import tempfile
import subprocess
import requests
import json
import os
import re
from gravityassist.bililib import get_bilibili_cookies, get_headers

def youtube_download(url, out_file=None):
    if not out_file:
        temp_dir = tempfile.gettempdir()
        watchid = re.search('v=([0-9a-zA-Z_-]+)', url)
        if watchid is None:
            watchid = re.search('youtu.be/([0-9a-zA-Z_-]+)', url)
            if watchid is None:
                watchid = 'unknown'
        if not isinstance(watchid, str):
            watchid = watchid.group(1)
        out_file = os.path.join(temp_dir, 'youtube-' + watchid + '.mp4')
    command = [
        'youtube-dl',
        '-f',
        'bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best',
        '-o',
        out_file,
        url,
    ]
    print('视频下载开始：{}'.format(' '.join(command)))
    subprocess.check_call(command)
    print('视频下载结束：{}'.format(out_file))
    return {'path': out_file}

def bilibili_download(url, out_file=None, mode='all', quality=0):
    cookies = get_bilibili_cookies()
    headers = get_headers()
    res_data = requests.get(url, cookies=cookies, headers=headers)
    # 获取视频信息
    pattern_video = '__playinfo__=(.*?)</script><script>'
    pattern_title = '<h1 title="(.*?)" class="video-title" data-v-'
    pattern_desc = '<div class="basic-desc-info" data-v-1d530b8d><span class="desc-info-text" data-v-1d530b8d>([\\u0000-\\uffff]*?)</span></div>'
    # 正则匹配
    match_video = re.search(pattern_video, res_data.text)
    match_title = re.search(pattern_title, res_data.text)
    match_desc = re.search(pattern_desc, res_data.text)
    assert match_video is not None
    assert match_title is not None
    title = match_title.group(1)
    assert isinstance(title, str), title
    desc = match_desc.group(1) if match_desc is not None else 'No description'
    if not out_file:
        temp_dir = tempfile.gettempdir()
        # bvid = re.search('/(BV[0-9a-zA-Z_-]+)', url)
        # if bvid is None:
        #     bvid = title.replace('/', '|')
        # else:
        #     bvid = bvid.group(1)
        # out_file = os.path.join(temp_dir, 'bilibili-' + bvid + '.mp4')
        out_file = os.path.join(temp_dir, 'bilibili-' + title.replace('/', '|') + '.mp4')
    print('视频标题：{}'.format(title))
    video_info_temp = json.loads(match_video.group(1))
    if 'dash' not in video_info_temp['data']:
        print(video_info_temp['data'])
        print('该视频没有 DASH！')
        return {'success': False, 'message': 'This video does not have any DASH tag. Maybe it is forbidden to download.'}
    qualinfo = video_info_temp['data']['dash']['video'][quality]
    print('视频清晰度：{}x{} {}帧'.format(qualinfo['width'], qualinfo['height'],
                                   qualinfo['frameRate']))
    video_time = int(video_info_temp['data']['dash']['duration'])
    video_url = video_info_temp['data']['dash']['video'][quality]['baseUrl']
    audio_url = video_info_temp['data']['dash']['audio'][quality]['baseUrl']
    video_minute = video_time // 60
    video_second = video_time % 60
    print('视频时长：{}分{}秒'.format(video_minute, video_second))
    if mode != 'description_only':
        if os.path.exists(out_file):
            return {'success': True, 'message': 'Video file already cached in local filesystem, not downloading.', 'path': out_file, 'description': desc, 'duration': '{:02d}:{:02d}'.format(video_minute, video_second)}
        vidheaders = dict(**headers)
        vidheaders.update({'Referer': url})
        if mode == 'all':
            videofile = tempfile.NamedTemporaryFile('wb')
            audiofile = tempfile.NamedTemporaryFile('wb')
        elif mode == 'audio_only':
            audiofile = open(out_file, 'wb')
            videofile = audiofile
        elif mode == 'silent_video':
            videofile = open(out_file, 'wb')
            audiofile = videofile
        else:
            raise ValueError('mode must be one of all, v, a')
        if mode in ['silent_video', 'all']:
            print('视频下载开始：{}'.format(video_url))
            video_content = requests.get(video_url,
                                         headers=vidheaders,
                                         cookies=cookies)
            print('视频大小：{} MB'.format(
                round(
                    int(video_content.headers.get('content-length', 0)) / 1024 /
                    1024, 3)))
            videofile.write(video_content.content)
            print('视频下载结束：{}'.format(videofile.name))
        if mode in ['audio_only', 'all']:
            print('音频下载开始：{}'.format(audio_url))
            audio_content = requests.get(audio_url,
                                         headers=vidheaders,
                                         cookies=cookies)
            print('音频大小：{} MB'.format(
                round(
                    int(audio_content.headers.get('content-length', 0)) / 1024 /
                    1024, 3)))
            audiofile.write(audio_content.content)
            print('音频下载结束：{}'.format(audiofile.name))
        if mode == 'all':
            command = [
                'ffmpeg',
                '-i',
                videofile.name,
                '-i',
                audiofile.name,
                '-c',
                'copy',
                out_file,
                '-y',
            ]
            print('视频合成开始：{}'.format(' '.join(command)))
            subprocess.check_call(command)
            print('视频合成结束：{}'.format(out_file))
            videofile.close()
            audiofile.close()
    if mode != 'description_only':
        return {'success': True, 'path': out_file, 'description': desc, 'duration': '{:02d}:{:02d}'.format(video_minute, video_second)}
    else:
        return {'success': True, 'title': title, 'description': desc, 'duration': '{:02d}:{:02d}'.format(video_minute, video_second), 'resolution': '{}x{} {}FPS'.format(qualinfo['width'], qualinfo['height'], qualinfo['frameRate'])}

def download_video(url: str, out_file: str | None = None, mode: str = 'all', quality: int = 0) -> dict:
    assert mode in ['all', 'silent_video', 'audio_only', 'description_only']
    if url.startswith('BV') or url.startswith('av'):
        url = 'https://www.bilibili.com/video/' + url
    if url.startswith('https://www.youtube.com/watch?v=') or url.startswith('https://youtu.be/'):
        return youtube_download(url, out_file)
    if url.startswith('https://www.bilibili.com/video/'):
        return bilibili_download(url, out_file, mode, quality)
    return {'success': False, 'message': 'Only Bilibili or YouTube video link is supported'}
